package com.lims;


import org.assertj.core.util.Lists;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author limaosheng
 * @create 2023-08-25 15:45
 * @desc
 */
public class 本服务自动读取表结构并生成代码 {
    public static void main(String[] args) throws Exception {
        List<String> tableNameList = Lists.newArrayList(
                "test_user");
        for(String tablename : tableNameList){
            gen(tablename);
        }
    }
    //数据库配置
    public final static String DB_URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
    public final static String DB_USER = "root";
    public final static String DB_PWD = "123456";

    //是否覆盖原文件
    public static final Boolean isOver = true;

    private static void gen(String tablename) throws Exception{
        //获取所有字段信息
        List<Map<String, Object>> list = getConlumns(tablename);
        //生成entity
        String entiyPath = System.getProperty("user.dir") + "/src/main/java/com/lims/base/entity/" + initcap(tablename) + ".java";
        genEntity(list, entiyPath,tablename);
        //生成mapper
        String mapperPath = System.getProperty("user.dir") + "/src/main/java/com/lims/base/mapper/" + initcap(tablename) + "Mapper.java";
        genMapper(mapperPath,tablename);
        //生成xml
        String xmlPath = System.getProperty("user.dir") + "/src/main/resources/mapper/" + initcap(tablename) + "Mapper.xml";
        genXml(xmlPath,tablename);
        //生成service
        String servicePath = System.getProperty("user.dir") + "/src/main/java/com/lims/service/I" + initcap(tablename) + "Service.java";
        genIService(servicePath,tablename);
        String implPath = System.getProperty("user.dir") + "/src/main/java/com/lims/service/impl/" + initcap(tablename) + "ServiceImpl.java";
        genServiceImpl(list, implPath,tablename);
        //生成controller
        String controllerPath = System.getProperty("user.dir") + "/src/main/java/com/lims/controller/" + initcap(tablename) + "Controller.java";
        genController(controllerPath,tablename);
    }


    private static void genController(String controllerPath,String tablename) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.lims.controller;\n" +
                "\n" +
                "import com.lims.base.entity." + initcap(tablename) + ";\n" +
                "import com.lims.common.BaseResult;\n" +
                "import com.lims.service.I" + initcap(tablename) + "Service;\n" +
                "import org.springframework.web.bind.annotation.PostMapping;\n" +
                "import org.springframework.web.bind.annotation.RequestBody;\n" +
                "import org.springframework.web.bind.annotation.RequestMapping;\n" +
                "import org.springframework.web.bind.annotation.RestController;\n" +
                "import jakarta.annotation.Resource;\n" +
                "\n" +
                "@RestController\n" +
                "@RequestMapping(\"/" + getCamelStr(tablename) + "\")\n" +
                "public class " + initcap(tablename) + "Controller {\n" +
                "    @Resource\n" +
                "    private I" + initcap(tablename) + "Service " + getCamelStr(tablename) + "Service;\n" +
                "\n" +
                "    @PostMapping(\"/getById\")\n" +
                "    public BaseResult<" + initcap(tablename) + "> getById(@RequestBody " + initcap(tablename) + " req) {\n" +
                "        return BaseResult.success(" + getCamelStr(tablename) + "Service.getById(req.getId()));\n" +
                "    }\n" +
                "}\n");
        write(controllerPath, sb.toString(), isOver);
    }

    private static void genServiceImpl(List<Map<String, Object>> list, String implPath,String tablename) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.lims.service.impl;\n" +
                "\n" +
                "import com.lims.base.mapper." + initcap(tablename) + "Mapper;\n" +
                "import com.lims.base.entity." + initcap(tablename) + ";\n" +
                "import com.lims.service.I" + initcap(tablename) + "Service;\n" +
                "import org.springframework.stereotype.Service;" +
                "import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;" +
                "\n" +
                "@Service\n" +
                "public class " + initcap(tablename) + "ServiceImpl extends ServiceImpl<" + initcap(tablename) + "Mapper, " + initcap(tablename) + "> implements I" + initcap(tablename) + "Service {\n" +
                "\n");
        sb.append(
                "}\n");
        write(implPath, sb.toString(), isOver);
    }

    private static void genIService(String servicePath,String tablename) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.lims.service;\n" +
                "\n" +
                "import com.baomidou.mybatisplus.extension.service.IService;\n" +
                "import com.lims.base.entity." + initcap(tablename) + ";\n" +
                "\n" +
                "public interface I" + initcap(tablename) + "Service extends IService<" + initcap(tablename) + "> {\n" +

                "}\n");
        write(servicePath, sb.toString(), isOver);
    }

    private static void genXml(String xmlPath,String tablename) {
        StringBuffer sb = new StringBuffer();
        sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n" +
                "<mapper namespace=\"com.lims.base.mapper." + initcap(tablename) + "Mapper\">\n" +
                "\n" +
                "</mapper>");
        write(xmlPath, sb.toString(), isOver);
    }

    private static void genMapper(String mapperPath,String tablename) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.lims.base.mapper;\n" +
                "\n" +
                "import com.baomidou.mybatisplus.core.mapper.BaseMapper;\n" +
                "import com.lims.base.entity." + initcap(tablename) + ";\n" +
                "\n" +
                "public interface " + initcap(tablename) + "Mapper extends BaseMapper<" + initcap(tablename) + "> {\n" +
                "}");
        write(mapperPath, sb.toString(), isOver);
    }


    private static void genEntity(List<Map<String, Object>> list, String entiyPath,String tablename) {
        StringBuffer sb = new StringBuffer();
        //获取表ID注解
        String idComment = "";
        String idType = "";
        for (Map<String, Object> map : list) {
            String column_name = map.get("COLUMN_NAME").toString();
            if ("id".equals(column_name)) {
                idComment = map.get("COLUMN_COMMENT").toString();
                String data_type = map.get("DATA_TYPE").toString();//不带长度，与column_type相比，没用括号以及括号内长度
                idType = sqlType2JavaType(data_type);
            }
        }
        boolean hasDate = false;
        boolean hasDatetime = false;
        for (Map<String, Object> map : list) {
            String data_type = map.get("DATA_TYPE").toString();//不带长度，与column_type相比，没用括号以及括号内长度
            if ("date".equals(data_type)) {
                hasDate = true;
            }
            if ("datetime".equals(data_type)) {
                hasDatetime = true;
            }
        }
        sb.append("package com.lims.base.entity;\n" +
                "\n" +
                "import com.baomidou.mybatisplus.annotation.IdType;\n" +
                "import com.baomidou.mybatisplus.annotation.TableId;\n" +
                "import com.fasterxml.jackson.annotation.JsonFormat;\n" +
                "import com.fasterxml.jackson.databind.annotation.JsonDeserialize;\n" +
                "import com.fasterxml.jackson.databind.annotation.JsonSerialize;\n");
        if (hasDatetime) {
            sb.append("import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;\n" +
                    "import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;\n" +
                    "import java.time.LocalDateTime;\n");
        }
        if (hasDate) {
            sb.append("import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;\n" +
                    "import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;\n" +
                    "import java.time.LocalDate;\n");
        }
        sb.append("import lombok.Data;\n" +
                "\n" +
                "import java.io.Serializable;\n" +
                "\n" +
                "/**\n" +
                " * 只用于 = 查询\n" +
                " */\n" +
                "@Data\n" +
                "public class " + initcap(tablename) + " implements Serializable {\n" +
                "    /**\n" +
                "     * " + idComment + "\n" +
                "     */\n" +
                "    @TableId(value = \"id\", type = IdType.AUTO)\n" +
                "\tprivate " + idType + " id;\n");
        for (Map<String, Object> map : list) {
            String column_name = map.get("COLUMN_NAME").toString();
            if ("id".equals(column_name)) {
                continue;
            }
            String colname = getCamelStr(column_name);//转成驼峰式
            String column_comment = map.get("COLUMN_COMMENT").toString();//备注
            String data_type = map.get("DATA_TYPE").toString();//不带长度，与column_type相比，没用括号以及括号内长度
            sb.append("    /**\r\n");
            sb.append("     * " + column_comment + "\r\n");
            sb.append("     */\r\n");
            String javaType = sqlType2JavaType(data_type);
            if ("date".equals(data_type)) {
                sb.append("    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"yyyy-MM-dd\", timezone = \"GMT+8\")\n" +
                        "    @JsonDeserialize(using = LocalDateDeserializer.class)\n" +
                        "    @JsonSerialize(using = LocalDateSerializer.class)\n");
            }else if ("datetime".equals(data_type)) {
                sb.append("    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = \"yyyy-MM-dd HH:mm:ss\", timezone = \"GMT+8\")\n" +
                        "    @JsonDeserialize(using = LocalDateTimeDeserializer.class)\n" +
                        "    @JsonSerialize(using = LocalDateTimeSerializer.class)\n");
            }
            sb.append("\tprivate " + javaType + " " + colname + ";\r\n");
        }
        sb.append(
                "\n" +
                "}\n");
        write(entiyPath, sb.toString(), isOver);
    }

    private static List<Map<String, Object>> getConlumns(String tablename) throws Exception {
        //获取表结构
        String colSql = "select column_name,column_comment,is_nullable,column_type,data_type,character_maximum_length,column_key from information_schema.columns where table_name= '" + tablename + "' and table_schema = 'smart_community'";
        Connection conn = null;
        List<Map<String, Object>> conlumns = new ArrayList<>();
        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PWD);
            PreparedStatement ps = conn.prepareStatement(colSql);
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData md = rs.getMetaData();
            int columnCount = md.getColumnCount();
            while (rs.next()) {
                Map<String, Object> rowData = new HashMap<String, Object>();
                for (int i = 1; i <= columnCount; i++) {
                    rowData.put(md.getColumnName(i), rs.getObject(i));
                }
                conlumns.add(rowData);
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
            throw throwables;
        } finally {
            closeConn(conn);
        }
        return conlumns;
    }

    public static void closeConn(Connection conn) {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 把输入字符串的首字母改成大写
     *
     * @param str
     * @return
     */
    public static String initcap(String str) {
        char[] ch = str.toCharArray();
        if (ch[0] >= 'a' && ch[0] <= 'z') {
            ch[0] = (char) (ch[0] - 32);
        }
        return getCamelStr(new String(ch));
    }

    // 例：user_name --> userName
    public static String getCamelStr(String s) {
        while (s.indexOf("_") > 0) {
            int index = s.indexOf("_");
            // System.out.println(s.substring(index+1, index+2).toUpperCase());
            s = s.substring(0, index) + s.substring(index + 1, index + 2).toUpperCase() + s.substring(index + 2);
        }
        return s;
    }

    public static String sqlType2JavaType(String sqlType) {
        String javaType = "String";
        if ("int".equals(sqlType)) {
            javaType = "Integer";
        } else if ("datetime".equals(sqlType)) {
            javaType = "LocalDateTime";
        } else if ("decimal".equals(sqlType)) {
            javaType = "BigDecimal";
        } else if ("bigint".equals(sqlType)) {
            javaType = "Long";
        } else if ("date".equals(sqlType)) {
            javaType = "LocalDate";
        }
        return javaType;
    }

    public static void write(String path,String data,Boolean isOver){
        try {
            System.out.println("文件地址：" + path);
            File writeName = new File(path); // 相对路径，如果没有则要建立一个新的output.txt文件
            if(writeName.exists()) {
                System.out.println("文件已存在");
                if(isOver){
                    System.out.println("覆盖原文件");
                    writeName.createNewFile();
                }else {
                    System.out.println("不覆盖原文件");
                    return;
                }
            }else {
                System.out.println("文件未存在");
                writeName.createNewFile();
            }
            FileWriter writer = new FileWriter(writeName);
            BufferedWriter out = new BufferedWriter(writer);
            out.write(data);
            out.flush(); // 把缓存区内容压入文件
            out.close();
        } catch (Exception e) {
            System.out.println("生成失败");
        }
    }
}
